<template>
  <!-- Element Plus 弹窗 -->
  <px-dialog
    v-model="dialogVisible"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    modal-class="px--visual-camera__dialog"
    width="600px"
    destroy-on-close
    :modal="false"
    title="摄像头播放"
    style="pointer-events: auto"
    @close="closeDialog"
  >
    <div class="dialog-body">
      <!-- 氣泡列表 -->
      <div class="bubble-list">
        <div class="bubbles">
          <px-tooltip v-for="(item, index) in visibleBubbles" :key="index + visibleIndex" effect="dark" placement="top" :content="item.eqpName">
            <div class="bubble" :class="{ selected: actualIndex(index) === selectedIndex }" @click="onBubbleClick(actualIndex(index))">
              <div class="bubble-text">
                {{ item.eqpName }}
              </div>
            </div>
          </px-tooltip>
        </div>
      </div>

      <!-- 内容区 -->
      <div class="content-container" @mouseover="hoveringContent = true" @mouseleave="hoveringContent = false">
        <!-- 左侧切换按钮 -->
        <div v-if="hoveringContent && selectedIndex > 0" class="nav-button left" @click.stop="goPrevContent">
          <px-icon><ArrowLeft /></px-icon>
        </div>
        <!-- 右侧切换按钮 -->
        <div v-if="hoveringContent && selectedIndex < dataList.length - 1" class="nav-button right" @click.stop="goNextContent">
          <px-icon><ArrowRight /></px-icon>
        </div>

        <div class="content-inner">
          <div v-show="cameraUrl" ref="flvRef" />
          <px-empty v-show="!cameraUrl" :image-size="200" />
        </div>
      </div>
    </div>
  </px-dialog>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, watch, nextTick, onUnmounted } from "vue";
import { queryMonitor } from "@/api/visual/visualConfig";

import useFlv from "@visual/utils/useFlv";
const cameraUrl = ref("");
const flvRef = ref();
const { getFlv, videoClick, destoryVideo } = useFlv({ videoRef: flvRef, cameraUrl: cameraUrl });
let removeListeners = null;

/**
 * 使用 Element Plus 实现拖拽弹框：
 * 1. <px-dialog> 提供弹窗
 * 2. 自定义 header 实现拖拽功能
 * 3. 氣泡列表超过 3 个时可滑动
 * 4. <px-tooltip> 显示超出气泡文字
 * 5. 下方小三角指示当前选中气泡
 */

interface BubbleItem {
  eqpName: string;
  rtspUrl: string;
}

// 示例数据，可根据实际需求替换
const dataList = ref<BubbleItem[]>([]);
const props = defineProps({
  positionCode: {
    type: String,
    required: true,
    default: ""
  },
  isInspection: {
    type: Boolean,
    required: true,
    default: false
  },
  playTime: {
    type: Number,
    required: false,
    default: 60
  }
});
// 控制弹窗显示
const dialogVisible = ref(false);
const isShow = defineModel("isShow", { default: false });
// 当前选中的气泡索引
const selectedIndex = ref<number>(0);
// 可视气泡的起始索引
const visibleIndex = ref<number>(0);
// 是否悬停内容区
const hoveringContent = ref(false);

// 计算当前显示的 3 个气泡
const visibleBubbles = computed(() => {
  return dataList.value.slice(visibleIndex.value, visibleIndex.value + 3);
});

// 是否可左右滑动
const canScrollLeft = computed(() => visibleIndex.value > 0);
const canScrollRight = computed(() => visibleIndex.value + 3 < dataList.value.length);
let timeFun = null;
watch(
  () => [props.positionCode, isShow.value],
  async val => {
    if (props.positionCode && props.isInspection && isShow.value) {
      const data = (await queryMonitor({ positionCode: props.positionCode })) as BubbleItem[];
      if (data && data.length) {
        dataList.value = data;
        selectedIndex.value = 0;
        visibleIndex.value = 0;
        nextTick(() => {
          dialogVisible.value = true;

          setFlv(data);
          if (!timeFun) {
            timeFun = createTimedExecutor(props.playTime, dataList.value.length, goNextContent, () => {
              setFlv(dataList.value, 0);
            });
            timeFun.start();
          } else {
            timeFun.start(props.playTime, dataList.value.length, goNextContent, () => {
              setFlv(dataList.value, 0);
            });
          }
          initDialogDrag();
        });
      } else {
        dialogVisible.value = false;
        reset();
      }
    } else {
      dialogVisible.value = false;
      reset();
    }
  },
  {
    immediate: true
  }
);
onUnmounted(() => {
  reset();
});
function reset() {
  destoryVideo();
  if (removeListeners) {
    removeListeners();
    removeListeners = null;
  }
  if (timeFun && timeFun.clear) timeFun.clear();
}
// header 按下开始拖拽
function pause() {
  if (timeFun && timeFun.pause) timeFun.pause;
}
function start() {
  if (timeFun && timeFun.start) timeFun.start;
}
// 左右滑动氣泡
function scrollBubbles(direction: "left" | "right") {
  if (direction === "left" && canScrollLeft.value) {
    visibleIndex.value -= 1;
  } else if (direction === "right" && canScrollRight.value) {
    visibleIndex.value += 1;
  }
}

// 点击气泡切换内容
function onBubbleClick(idx: number) {
  selectedIndex.value = idx;
  ensureSelectedVisible();
}

// 保证选中气泡在可视范围
function ensureSelectedVisible() {
  if (selectedIndex.value < visibleIndex.value) {
    visibleIndex.value = selectedIndex.value;
  } else if (selectedIndex.value >= visibleIndex.value + 3) {
    visibleIndex.value = selectedIndex.value - 2;
  }
}

// 内容区上一页
function goPrevContent() {
  if (selectedIndex.value > 0) {
    selectedIndex.value--;
    ensureSelectedVisible();
    setFlv(dataList.value, selectedIndex.value);
  }
}

// 内容区下一页
function goNextContent() {
  if (selectedIndex.value < dataList.value.length - 1) {
    selectedIndex.value++;
    ensureSelectedVisible();
    setFlv(dataList.value, selectedIndex.value);
  }
}

// 根据可视气泡 index 计算实际索引
function actualIndex(relativeIndex: number) {
  return visibleIndex.value + relativeIndex;
}
function closeDialog() {
  dialogVisible.value = false;
  isShow.value = false;
  reset();
}
function setFlv(data, index = 0) {
  cameraUrl.value = data[index].rtspUrl;
  // cameraUrl.value = "/data/ifp/file/file/1.mp4";
  getFlv();
}
function createTimedExecutor(initialTotalSeconds, initialCount, initialCallback, initialInit) {
  let callback = initialCallback;
  let totalSeconds = initialTotalSeconds;
  let count = initialCount;
  let init = initialInit;

  let interval = (totalSeconds * 1000) / count;
  let timeouts = [];
  let executedCount = 0;
  let isPaused = false;
  let startTime = null;
  let pauseTime = null;
  let remainingIntervals = [];

  function schedule(startOffset = 0) {
    startTime = Date.now() - startOffset;
    for (let i = executedCount; i < count; i++) {
      const delay = i * interval - startOffset;

      if (delay <= 0 && i === executedCount) {
        if (!isPaused) {
          if (i === 0 && init) {
            init(); // Only first run executes init
          } else {
            callback(i);
          }
          executedCount++;
        }
      } else {
        const timeoutId = setTimeout(() => {
          if (!isPaused) {
            if (i === 0 && init) {
              init();
            } else {
              callback(i);
            }
            executedCount++;
          }
        }, delay);
        timeouts.push(timeoutId);
        remainingIntervals.push(delay);
      }
    }
  }

  function clearAll() {
    timeouts.forEach(clearTimeout);
    timeouts = [];
    executedCount = 0;
    isPaused = false;
    startTime = null;
    pauseTime = null;
    remainingIntervals = [];
  }

  return {
    start(newTotalSeconds, newCount, newCallback, newInit) {
      const isNewParams = newTotalSeconds !== undefined || newCount !== undefined || newCallback !== undefined || newInit !== undefined;

      if (isNewParams) {
        clearAll();
        if (newTotalSeconds !== undefined) totalSeconds = newTotalSeconds;
        if (newCount !== undefined) count = newCount;
        if (newCallback !== undefined) callback = newCallback;
        if (newInit !== undefined) init = newInit;
        interval = (totalSeconds * 1000) / count;
      }

      if (timeouts.length === 0 && executedCount === 0) {
        schedule(0);
      }
    },
    pause() {
      if (!isPaused) {
        isPaused = true;
        pauseTime = Date.now();
        timeouts.forEach(clearTimeout);
        timeouts = [];
        remainingIntervals = remainingIntervals.slice(executedCount);
      }
    },
    resume() {
      if (isPaused) {
        const elapsedSinceStart = pauseTime - startTime;
        isPaused = false;
        schedule(elapsedSinceStart);
      }
    },
    clear() {
      clearAll();
    }
  };
}
const initDialogDrag = () => {
  nextTick(() => {
    const dialog = document.querySelector(".px-dialog") as HTMLElement;
    const header = document.querySelector(".px-dialog__header") as HTMLElement;

    dialog.style.position = "absolute";
    dialog.style.top = "-6px";
    dialog.style.left = "644px";

    header.style.cursor = "move";

    let isDragging = false;
    let startX = 0;
    let startY = 0;
    let initLeft = 0;
    let initTop = 0;

    const onMouseDown = (e: MouseEvent) => {
      isDragging = true;
      startX = e.clientX;
      startY = e.clientY;
      initLeft = parseFloat(dialog.style.left);
      initTop = parseFloat(dialog.style.top);
      document.addEventListener("mousemove", onMouseMove);
      document.addEventListener("mouseup", onMouseUp);
    };

    const onMouseMove = (e: MouseEvent) => {
      if (!isDragging) return;
      const dx = e.clientX - startX;
      const dy = e.clientY - startY;
      dialog.style.left = initLeft + dx + "px";
      dialog.style.top = initTop + dy + "px";
    };

    const onMouseUp = () => {
      isDragging = false;
      document.removeEventListener("mousemove", onMouseMove);
      document.removeEventListener("mouseup", onMouseUp);
    };

    header.addEventListener("mousedown", onMouseDown);
    removeListeners = () => {
      header.removeEventListener("mousedown", onMouseDown);
      document.removeEventListener("mousemove", onMouseMove);
      document.removeEventListener("mouseup", onMouseUp);
    };
  });
};
defineExpose({ start, pause });
</script>
<style>
.px--visual-camera__dialog {
  margin-bottom: 0;
  pointer-events: none;

  .px-dialog__header {
    padding: 16px;
    background-color: #f7f7f7;
  }

  .px-dialog {
    padding-bottom: 16px;
    transform: translate(-419px, 33px);
  }

  .px-dialog__body {
    padding-top: 10px;
    padding-right: 16px;
    padding-left: 16px;
  }
}
</style>
<style lang="scss" scoped>
/* 内容区主体 */
.dialog-body {
  display: flex;
  flex-direction: column;
  height: auto; /* 减去标题栏高度 */
}

/* 顶部气泡列表 */
.bubble-list {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}

.bubble-button {
  padding: 0 8px;
  font-size: 18px;
  cursor: pointer;
  user-select: none;
}

.bubbles {
  display: flex;
  flex: 1;
  width: 100%;
}

/* 氣泡：等分父容器宽度（一次显示3个） */
.bubble {
  position: relative;
  box-sizing: border-box;
  display: flex;
  width: 31%;
  padding: 8px;
  margin-right: 5px;
  color: #000;
  text-align: center;
  cursor: pointer;
  background: #f7f7f7;
  border-radius: 4px;
}

.bubble.selected {
  color: #3b82f6; /* 选中文字 */
  background: #ecf5ff; /* 选中背景 */
}

/* 底部小三角：选中时出现 */
.bubble.selected::after {
  position: absolute;
  bottom: -12px;
  left: 50%;
  width: 0;
  height: 0;
  content: "";
  border-top: 12px solid #ecf5ff; /* 与选中背景一致 */
  border-right: 12px solid transparent;
  border-left: 12px solid transparent;
  transform: translateX(-50%);
}

.bubble-text {
  flex: 1; /* 占据剩余空间 */
  min-width: 0; /* 关键：允许宽度压缩 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 内容区 */
.content-container {
  position: relative;
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* 左右切换按钮 */
.nav-button {
  position: absolute;
  top: 50%;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: #fff;
  cursor: pointer;
  user-select: none;
  background: rgb(0 0 0 / 30%);
  border-radius: 50%;
  transform: translateY(-50%);
}

.nav-button.left {
  left: 8px;
}

.nav-button.right {
  right: 8px;
}

.content-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
</style>
